package nacos_authoa.config;

import nacos_authoa.common.GlobalExceptionResolver;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisClusterManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.crazycake.shiro.RedisCacheManager;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    /**
     * 注入配置文件属性
     */

    @Value("${redis.host}")
    private String host;

    @Value("${redis.port}")
    private int port;

    @Value("${redis.password}")
    private String password;

    @Value("${redis.timeout}")
    private int timeout;

    /*1、创建自定义对戏那个*/
    @Bean("userRealm")
    public UserRealm  getUserRealm(){
        UserRealm userRealm = new UserRealm();
        userRealm.setCachingEnabled(true);
        //启用身份验证缓存，即缓存AuthenticationInfo信息，默认false
        userRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称
        userRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存，即缓存AuthorizationInfo信息，默认false
        userRealm.setAuthorizationCachingEnabled(true);
        // //缓存AuthorizationInfo信息的缓存名称
        userRealm.setAuthorizationCacheName("authorizationCache");
        //配置自定义的密码比较器

        return userRealm;
    }

    /**
     * 安全管理器
     * @param userRealm
     * @return
     */
    @Bean("securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //设置自定义Realm
        securityManager.setRealm(userRealm);
        //设置记住我
//        securityManager.setRememberMeManager(rememberMeManager());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    //创建工厂
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(securityManager);
        Map<String,String> map=new HashMap<>();
        map.put("/shiroGetTest","perms[test:select]");
        map.put("/getData","perms[getData:select]");
//        map.put("/provider01/test/proTest","authc");
        map.put("/isPermitted","anon");
//        map.put("/nacos-authoa/login","anon");

        bean.setFilterChainDefinitionMap(map);

        bean.setLoginUrl("/toLogin");
        return bean;


    }
    /**
     * Shiro生命周期处理器
     * 此方法需要用static作为修饰词，否则无法通过@Value()注解的方式获取配置文件的值
     *
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启shiro的注解模式，
     * 例如@RequirePermissions注解
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
//
//    /**
//     * cookie对象；会话Cookie模板，默认为：JSESSIONID，问题：与servlet容器名冲突，
//     * 重新定义为sid或者rememberMe,自定义
//     */
//    @Bean
//    public SimpleCookie rememberMeCookie(){
//        //这个参数是cookie的名称，对应前端的checkbox的name=rememberMe
//        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
////        simpleCookie.setHttpOnly(true);
////        simpleCookie.setPath("/");
//        //记住我cookie生效天数，单位秒
//        simpleCookie.setMaxAge(3600);
//        return simpleCookie;
//    }
//    /**
//     * cookie管理对象，记住我功能，rememberMe管理器
//     * @return
//     */
//    @Bean
//    public CookieRememberMeManager rememberMeManager(){
//        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//        cookieRememberMeManager.setCookie(rememberMeCookie());
//        //加密密钥
//        cookieRememberMeManager.setCipherKey(Base64.decode("aabbcaa"));
//        return cookieRememberMeManager;
//    }
//


    //配置全局异常处理
    @Bean(name = "exceptionHandler")
    public HandlerExceptionResolver handlerExceptionResolver(){
        return new GlobalExceptionResolver();
    }


    /**
     * redis集群管理器
     * @return
     */
//    @Bean
//    public RedisClusterManager redisClusterManager() {
//        RedisClusterManager redisClusterManager = new RedisClusterManager();
//        redisClusterManager.setHost(this.host);
//
//        return redisClusterManager;
//    }


    /**
     * redis 池子
     * redis集群管理器
     * @return
     */

    @Bean
    public JedisPool redisPoolFactory() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password,6);

        return jedisPool;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setJedisPool(redisPoolFactory());
        redisManager.setPassword(password);
        redisManager.setTimeout(timeout);
        return redisManager;
    }





    /**
     * shiro缓存管理器;
     * 需要添加到securityManager中
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        // 针对不同的用户缓存，由于principal是SysUserMsg，所以需是里面的字段
        redisCacheManager.setKeyPrefix("username");   //设置前缀
        //用户权限信息缓存时间
        redisCacheManager.setExpire(3600);

        return redisCacheManager;
    }


//    /**
//     * 配置session监听
//     * @return
//     */
//    @Bean("sessionListener")
//    public ShiroSessionListener sessionListener(){
//        ShiroSessionListener sessionListener = new ShiroSessionListener();
//        return sessionListener;
//    }

    /**
     * 配置会话ID生成器
     * @return
     */
    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    /**
     *  先配置保存sessionId的cookie
     *  注意：这里的cookie不是上面的记住我cookie 记住我需要一个cookie session管理，也需要自己的cookie
     *  默认为：JSESSIONID 问题：与servlet容器名冲突，重新定义为sid或者token
     * @return
     */
    @Bean("simpleCookie")
    public SimpleCookie simpleCookie(){
        //这个参数是cookie的名称
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：

        //setcookie()的第七个参数
        //设为true后，只能通过http访问，javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //maxAge=-1表示浏览器关闭时失效此Cookie
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * Session User(会话管理器
     *
     * 先配置保存sessionId的cookie
     * 注意：这里的cookie不是上面的记住我cookie 记住我需要一个cookie session管理，也需要自己的cookie
     * 默认为：JSESSIONID 问题：与servlet容器名冲突，重新定义为sid或者token
     * 再配置会话管理器
     */
    @Bean
    public SessionManager sessionManager() {

        SessionManager sessionManager = new SessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setSessionIdCookieEnabled(true);
        //取消url 后面的 JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        //是否开启删除无效的session对象  默认为true
        sessionManager.setDeleteInvalidSessions(true);
        //是否开启定时调度器进行检测过期session 默认为true
        sessionManager.setSessionValidationSchedulerEnabled(true);

        sessionManager.setSessionIdCookie(simpleCookie());
        //全局会话超时时间（单位毫秒），默认30分钟  暂时设置为10秒钟 用来测试
        sessionManager.setGlobalSessionTimeout(3600 * 1000);//单位毫秒
        return sessionManager;
    }




    /**
     * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
     * MemorySessionDAO 直接在内存中进行会话维护
     * EnterpriseCacheSessionDAO  提供了缓存功能的会话维护，默认情况下使用MapCache实现，内部使用ConcurrentHashMap保存缓存的会话。
     * @return
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
//        redisSessionDAO.setKeyPrefix("SPRINGBOOT_SESSION:");
        redisSessionDAO.setExpire(3600);//设置过期时间，单位s
        return redisSessionDAO;
    }

    /***
     * 授权所用配置
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }





}
